#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
// 找最后一个/ ，宏是替换可以不用传二级指针，do while 不加分号，为了后面加分号
#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)

char* gArgv[NUM];
int lastcode = 0;
char cwd[SIZE*2];

const char* GetUserName()
{
  const char* name = getenv("USER");
  if(name == NULL) return "None";
  return name;
}

const char* GetHostName()
{
  const char* hostname = getenv("HOSTNAME");
  if(hostname == NULL) return "None";
  return hostname;
}

const char* GetCwd()
{
  const char* cwd = getenv("PWD");
  if(cwd == NULL) return "None";
  return cwd;
}

const char* GetHome()
{
  const char* home = getenv("HOME");
  if(home == NULL) return "None";
  return home;
}

// command : output
void  MakeCommandLineAndPrint()
{
  char line[SIZE];
  // 用户名@主机名 当前路径
  const char* username = GetUserName();
  const char* hostname = GetHostName();
  const char* cwd = GetCwd();

  SkipPath(cwd);
  // 需要处理第一个/，长度为1用/
  snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,strlen(cwd) == 1 ? "/" : cwd + 1);
  printf("%s",line);
  fflush(stdout);
}


int GetUserCommand(char command[],size_t n)
{
  char* s = fgets(command,n,stdin);
  if(s == NULL) return -1;
  command[strlen(command)-1]=ZERO;
  return strlen(command);
}

void SplitCommand(char command[])
{
  // "ls -a -l -n" -> "ls" "-a" "-l" "-n"
  gArgv[0]=strtok(command,SEP);
  int index = 1;
  // 故意写成赋值，表示先赋值再判断，分割之后返回NULL，刚好让gArgv的最后一个元素为空，循环结束
  while((gArgv[index++] = strtok(NULL,SEP)));
}

//void SplitCommand(char command[],size_t n)
//{
//  //void(n);
//  // "ls -a -l -n" -> "ls" "-a" "-l" "-n"
//  gArgv[0]=strtok(command,SEP);
//  int index = 1;
//  // 故意写成赋值，表示先赋值再判断，分割之后返回NULL，刚好让gArgv的最后一个元素为空，循环结束
//  while((gArgv[index++] = strtok(NULL,SEP)));
//}

void Die()
{
  exit(1);
}

void  ExecuteCommand()
{
  pid_t id = fork();
  if(id < 0) Die();
  else if(id == 0)
  {
    // child 程序替换
    execvp(gArgv[0],gArgv);
    exit(errno);
  }
  else 
  {
    // father
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
      lastcode = WEXITSTATUS(status);
      if(lastcode != 0) printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
    }
  }
}

void Cd()
{
  const char* path = gArgv[1];
  if(path == NULL) path = GetHome();
  // path 一定存在
  chdir(path);

  // 刷新环境变量
  char temp[SIZE*2];
  getcwd(temp,sizeof(temp));
  snprintf(cwd,sizeof(cwd),"PWD=%s",temp);
  putenv(cwd);
}
int CheckBuildin()
{
  int yes = 0;
  const char* enter_cmd = gArgv[0];
  if(strcmp(enter_cmd,"cd") == 0)
  {
    yes = 1;
    Cd();
  }
  else if(strcmp(enter_cmd,"echo") == 0 && strcmp(gArgv[1],"$?") == 0)
  {
    yes = 1;
    printf("%d\n",lastcode);
    lastcode = 0;
  }
  return yes;
}
int main()
{
  //// 1、自己输出一个命令行
  //MakeCommandLineAndPrint();
  ////2、获取用户命令字符串
  //char usercommand[SIZE];
  //int n = GetUserCommand(usercommand,sizeof(usercommand));
  //if(n<=0) return 1;// 没有获取到字符串则结束程序
  //printf("%s\n",usercommand);
  //// 3、命令行字符串分割
  //SplitCommand(usercommand);
  //for(int i=0;gArgv[i];i++)
  //{
  //  printf("%s\n",gArgv[i]);
  //}
  //// 4、检测命令是否是内建命令
  //n = CheckBuildin();
  //printf("n = %d\n",n);
  //// 5、执行命令
  //ExecuteCommand();
  int quit = 0;
  while(!quit)
  {
    // 1、自己输出一个命令行
    MakeCommandLineAndPrint();
    // 2、获取用户命令字符串
    char usercommand[SIZE];
    int n = GetUserCommand(usercommand,sizeof(usercommand));
    if(n<=0) return 1;
    // 3、命令行字符串分割
    SplitCommand(usercommand);
    // 4、检测命令是否是内建命令
    n = CheckBuildin();
    if(n) continue;
    // 5、执行命名
    ExecuteCommand();
  }
  return 0;
}
